﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Data;
using System.Windows.Forms;
using System.IO;
using System.Drawing;
using System.Drawing.Imaging;
using Npgsql;
namespace SpectationClient.Stuff {
    static class DataHelper {

        /// <summary>
        /// Opens a file using the standard program related to the specific file format.
        /// </summary>
        /// <param name="filepath"></param>
        public static void openFile(String filepath) {
            DataHelper.openFile(new FileInfo(filepath));
        }

        /// <summary>
        /// Opens a file using the standard program related to the specific file format.
        /// </summary>
        /// <param name="file"></param>
        public static void openFile(FileInfo file) {

            if(file.Exists) {
                try {
                    System.Diagnostics.Process.Start(file.FullName);
                } catch(Exception ex) {
                    FormHelper.ShowErrorBox(ex);
                }
            } else {
                MessageBox.Show(String.Format("Die Datei {0} konnte nicht gefunden werden", file.FullName),
                        "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
        }

        public static FileInfo createNewFilePath(FileInfo fileName) {
            int v = 0;
            return DataHelper.createNewFilePath(fileName, ref v);

        }
        /// <summary>
        /// Returns a defenitely new file name.
        /// </summary>
        /// <param name="baseName"></param>
        /// <param name="extension"></param>
        /// <param name="version"></param>
        /// <returns></returns>
        public static FileInfo createNewFilePath(FileInfo fileName, ref int version) {
            String baseName = fileName.FullName;
            if(!String.IsNullOrEmpty(fileName.Extension)) {
                baseName = baseName.Remove(baseName.Length - fileName.Extension.Length, fileName.Extension.Length);
                //baseName = Regex.Replace(fileName.FullName, @"\.[^.]+$", "", RegexOptions.IgnoreCase);
            }

            String path = null;
            do {
                path = DataHelper.createFileBaseName(baseName, version++, fileName.Extension);
            } while(File.Exists(path));
            return new FileInfo(path);
        
        }

        private static String createFileBaseName(String baseName, int version, String extension=null){
            if(version > 0) baseName+= version.ToString();
            return extension == null? baseName : baseName + extension;
        }

        /// <summary>
        /// Saves an image to a local filepath in the format extracted by the file extension from the filename.
        /// </summary>
        /// <param name="filename">filepath with extension png, jpg, tiff or png. Default is png</param>
        /// <param name="image">The image to save</param>
        public static void saveImage(String filename, Image image) {
            DataHelper.saveImage(filename, image, DataHelper.parseImageFormat(filename));
        }

        /// <summary>
        /// Saves an image to a local filepath in the format extracted by the file extension from the filename.
        /// </summary>
        /// <param name="filename">Filepath with extension png, jpg, tiff or png. Default is png</param>
        /// <param name="image">The image to save</param>
        /// <param name="imageFormat"></param>
        public static void saveImage(String filename, Image image, ImageFormat imageFormat) {

            FileInfo fileInfo = new FileInfo(filename);
            String extension = fileInfo.Extension.ToLower();
            System.Drawing.Imaging.ImageFormat format = System.Drawing.Imaging.ImageFormat.Png;

           
            image.Save(filename, imageFormat);
        }

        /// <summary>
        /// An image format filter list to be used in SaveFile Dialogs.
        /// </summary>
        public static String ImageFormatFilter {
            get { return "JPG (*.jpg)|*.jpg|PNG (*.png)|*.png|BMP (*.bmp)|*.bmp|WMF (*.wmf)|*.wmf|GIF (*.gif)|*.gif|TIFF (*.tif)|*.tif"; }
        
        }
        /// <summary>
        /// An image format filter list to be used in SaveFile Dialogs.
        /// </summary>
        public static List<BoxItem> ImageFormatFilters {
            get {
                String[] pairs = DataHelper.ImageFormatFilter.Split(new char[] { '|' });
                List<BoxItem> list = new List<BoxItem>();
                for(int i = 0; i < pairs.Length - 1; i +=2) {
                    list.Add(new BoxItem(pairs[i], DataHelper.parseImageFormat(pairs[i+1])));
                }
                return list;
            }
        }


        /// <summary>
        /// Parses a string for a image file extension and returns the corresponding ImageFormat.
        /// E.g. "xyz.jpg" will return ImageFormat.JPG
        /// </summary>
        /// <param name="fileExtension">String with image file extension</param>
        /// <returns></returns>
        public static ImageFormat parseImageFormat(String fileExtension) {
            fileExtension = fileExtension.ToLower();
            fileExtension = Regex.Match(fileExtension, "[^.]+$").Value;
            if(fileExtension == null) return ImageFormat.Png;
            ImageFormat format;
            switch(fileExtension) {
                case "jpg": format = ImageFormat.Jpeg; break;
                case "bmp": format = ImageFormat.Bmp; break;
                case "wmf": format = ImageFormat.Wmf; break;
                case "emf": format = ImageFormat.Emf; break;
                case "exif": format = ImageFormat.Exif; break;
                case "gif": format = ImageFormat.Gif; break;
                case "icon": format = ImageFormat.Icon; break;
                case "tiff":
                case "tif": format = ImageFormat.Tiff; break;
                default: format = ImageFormat.Png; break;
            }
            return format;
        }

        public static void removeEmptyValues(ref Dictionary<String, object> dic) {
            List<String> toRemove = new List<string>();
            foreach(String s in dic.Keys) { 
                if(dic[s] == null || dic[s] == DBNull.Value ||
                   dic[s].GetType() == typeof(string) && ((string)dic[s]).Trim().Length == 0)
                    toRemove.Add(s);
            }
            foreach(String s in toRemove) {
                dic.Remove(s);
            }
        }

        public static void removeEmptyValues(ref DataRow row) {
            for(int i = 0; i < row.ItemArray.Length; i++) {
                if(row[i].ToString().Trim().Length == 0) row[i] = DBNull.Value;
            }
        }



        public static T parseEnum<T>(ToolStripComboBox comboBox) {
            return (T)Enum.Parse(typeof(T), comboBox.SelectedItem.ToString());
        }
        public static T parseEnum<T>(ComboBox comboBox) {
            return (T)Enum.Parse(typeof(T), comboBox.SelectedItem.ToString());
        }

        /// <summary>
        /// Returns the string representation of an enumeration value.
        /// </summary>
        /// <param name="enumerationValue"></param>
        /// <returns></returns>
        public static String enumAsString(Enum enumerationValue) {
            return Enum.GetName(enumerationValue.GetType(), enumerationValue);
        
        }

        /// <summary>
        /// Removes all NpgsqlCommand Elements that are set to NULL or have an empty command text.
        /// </summary>
        /// <param name="cmdList"></param>
        public static void removeEmptyValues(ref List<NpgsqlCommand> cmdList) {
            int i = 0;
            while(i < cmdList.Count) {
                if(cmdList[i] == null || cmdList[i].CommandText.Trim().Length == 0) {
                    cmdList.RemoveAt(i);
                } else {
                    i++;
                }
            }
        }

        public static void removeEmptyValues(ref DataTable table) {
            for(int i = 0; i < table.Rows.Count; i++) {
                DataRow row = table.Rows[i];
                DataHelper.removeEmptyValues(ref row);
            }
        }

        public static bool isEmpty(DataRow row) {
            for(int i = 0; i < row.ItemArray.Length; i++) {
                if(row[i] != null &&
                    row[i] != DBNull.Value &&
                    row[i].ToString().Trim().Length != 0) {
                    return false;
                }
            }
            return true;
        }

        #region Converters to Nullable Type Arrays
        public static Int16?[] ConvertToNullable(Int16[] array) {
            return (from a in array
                    select a as Int16?).ToArray();
           
        }
        public static Int32?[] ConvertToNullable(Int32[] array) {
            return (from a in array
                    select a as Int32?).ToArray();

        }
        public static Int64?[] ConvertToNullable(Int64[] array) {
            return (from a in array
                    select a as Int64?).ToArray();

        }
        public static Single?[] ConvertToNullable(Single[] array) {
            return (from a in array
                    select a as Single?).ToArray();

        }
        public static Double?[] ConvertToNullable(Double[] array) {
            return (from a in array
                    select a as Double?).ToArray();

        }
        #endregion

        public static bool CanConvert(Type targetType, object value) {
            if(targetType == null) return false;
            if(value == null || value is DBNull) return false;
            if(targetType == value.GetType()) return true;

            if(String.IsNullOrWhiteSpace(value.ToString())) return false;

            IConvertible convertible = value as IConvertible;
            if(convertible == null) return false;

            bool result = true;
            try {
                object o = Convert.ChangeType(value, targetType);
                result = true;
            } catch(Exception) {
                result = false;
            }
            return result;
        }

        public static bool isEmpty(DataRow row, String column) {
            return row[column] != null && row[column] != DBNull.Value && row[column].ToString().Trim().Length > 0;
        }

        public static bool isEmpty(DataRow row, int column) {
            return row[column] != null && row[column] != DBNull.Value && row[column].ToString().Trim().Length > 0;
        }

        /// <summary>
        /// Returns true when the list contains attributes least one NpgsqlCommand that is not null and has a
        /// trimmed command-string longer zero.
        /// </summary>
        /// <param name="cmdList"></param>
        /// <returns></returns>
        public static bool isEmpty(List<NpgsqlCommand> cmdList) {
            foreach(NpgsqlCommand cmd in cmdList) {
                if(cmd != null && cmd.CommandText.Trim().Length > 0) return false;
            }
            return true;
        }

        public static bool isEmpty(DataGridViewRow row) {
            foreach(DataGridViewCell C in row.Cells) {
                if(C.Value != null &&
                   C.Value != DBNull.Value &&
                   C.Value.ToString().Trim().Length != 0) {
                    return false;
                }
            }
            return true;
        }

        public static bool isEmpty(Dictionary<String, Object> dic) {
            foreach(String s in dic.Keys) {
                Object o = dic[s];
                if(o != null && o != DBNull.Value && o.ToString().Trim().Length > 0) return false;
            }
            return true;
        }

        public static bool isEmpty(DataTable table) {
            foreach(DataRow row in table.Rows) {
                if(!DataHelper.isEmpty(row)) return false;
            }
            return true;
        }



        public static void removeEmptyRows(ref DataTable dt) {
            int i = 0;
            while(i < dt.Rows.Count) {
                if(DataHelper.isEmpty(dt.Rows[i])) {
                    dt.Rows.RemoveAt(i);
                } else {
                    i++;
                }
            }
        }


        
        private static bool ValueExists(DataTable dt, String column, Object valueToFind, out int rowWithEntry) {
            foreach(DataRow r in dt.Rows) {
                if(r[column] == valueToFind) {
                    rowWithEntry = dt.Rows.IndexOf(r);
                    return true;
                }
            }
            rowWithEntry = -1;
            return false;
        }

        public static void addDicTo(ref Dictionary<String, object> dic, DataRow dr) {
            foreach(String s in dic.Keys) {
                Object o = dic[s];
                if(o != null && o != DBNull.Value && o.ToString().Trim().Length > 0) dr[s] = o;
            }
        }

        /// <summary>
        /// Returns a list of indices that show which rows from talbe B are contained in tableName A
        /// </summary>
        /// <param name="tableA"></param>
        /// <param name="tableB"></param>
        /// <returns></returns>
        public static List<DataRow> EqualRows(DataTable tableA, DataTable tableB) {
            List<DataRow> redundantRows = new List<DataRow>();
            if(!DataHelper.ColumnsEqual(tableA.Columns, tableB.Columns)) throw new Exception("Columns are not equal");
            foreach(DataRow rA in tableA.Rows) {
                foreach(DataRow rB in tableB.Rows) {
                    bool is_equal = true;
                    bool test_equal = rA.Equals(rB);
                    for(int c = 0; c < tableA.Columns.Count; c++ ) { 
                        if(!rA[c].Equals(rB[c])){
                            is_equal = false;
                            break;
                        }
                    }
                    if(is_equal) redundantRows.Add(rB);
                }
            }
            return redundantRows;
        }

        public static bool ColumnsEqual(DataColumnCollection A, DataColumnCollection B) {
            return DataHelper.ColumnsEqual(A, B, false);
        }
        public static bool ColumnsEqual(DataColumnCollection A, DataColumnCollection B, bool checkNotNULL) {
            bool ret = true;
            if(A.Count != B.Count) return false;
            foreach(DataColumn cA in A) {
                if(!B.Contains(cA.ColumnName)) return false;
                if(checkNotNULL && !(cA.AllowDBNull != B[cA.ColumnName].AllowDBNull)) return false;
            }
            return ret;
        }

        public static void print(DataTable dt) {
            String[] cn = dt.Columns.Cast<DataColumn>().Select(x => x.ColumnName).ToArray();
            for(int i = 0; i < cn.Length; i++) Console.Write("{0} ",cn[i]);
            Console.WriteLine();
            foreach(DataRow r in dt.Rows) {
                Object[] ar = r.ItemArray;
                for(int i = 0; i < ar.Length; i++) Console.Write("{0} ", ar[i]);
                Console.WriteLine();
            }
            Console.WriteLine();

        }

        # region Graphic Tools
        private static Size getScaledSize(Size size, int maxH, int maxW) {
            Size newsize = size;
            if(!size.IsEmpty) {
                float rfH = (float)maxH / (float)size.Height; //Reducing factors
                float rfW = (float)maxW / (float)size.Width;
                if(rfH < 1 || rfW < 1) { //if factor < 1 --> size too big
                    float rf = Math.Min(rfH, rfW);
                    newsize.Height = (int)(size.Height * rf);
                    newsize.Width = (int)(size.Width * rf);
                }
            }
            return newsize;
        }

        public static Image makePreview(Byte[] bytea, int maxX, int maxY) {
            return DataHelper.makePreview(DataHelper.ByteArrayToImage(bytea), maxX, maxY);
        }

        public static Image makePreview(Image input, int maxX, int maxY) {
            Size size = getScaledSize(input.Size, maxX, maxY);
            Bitmap oldImg = (Bitmap)input;
            Bitmap newImg = new Bitmap(input, size);//size.Width, size.Height, PixelFormat.Format24bppRgb);
            return (Image)newImg;
        
        }

        public static Image ByteArrayToImage(byte[] byteArray) {
            if(byteArray != null) {
                MemoryStream stream = new MemoryStream(byteArray);
                Image im = Image.FromStream(stream);

                return im;

            }
            return null;
        }


        public static ImageFormat getImageFormat(FileInfo fileInfo) {
            int nBytes = 10;
            if (fileInfo.Length < nBytes) return null;
            FileStream f = fileInfo.OpenRead();
            Byte[] array = new Byte[nBytes];
            f.Read(array, 0, nBytes);
            f.Close();
            return getImageFormat(array);
        
        }

        public static ImageFormat getImageFormat(byte[] bytes) {
            // see http://www.mikekunz.com/image_file_header.html  
            var bmp    = Encoding.ASCII.GetBytes("image");     // BMP
            var gif    = Encoding.ASCII.GetBytes("GIF");    // GIF
            var png    = new byte[] { 137, 80, 78, 71 };    // PNG
            var tiff   = new byte[] { 73, 73, 42 };         // TIFF
            var tiff2  = new byte[] { 77, 77, 42 };         // TIFF
            var jpeg   = new byte[] { 255, 216, 255, 224 }; // jpeg
            var jpeg2  = new byte[] { 255, 216, 255, 225 }; // jpeg canon

            if(bmp.SequenceEqual(bytes.Take(bmp.Length)))
                return System.Drawing.Imaging.ImageFormat.Bmp;

            if(gif.SequenceEqual(bytes.Take(gif.Length)))
                return System.Drawing.Imaging.ImageFormat.Gif;

            if(png.SequenceEqual(bytes.Take(png.Length)))
                return System.Drawing.Imaging.ImageFormat.Png;

            if(tiff.SequenceEqual(bytes.Take(tiff.Length)))
                return System.Drawing.Imaging.ImageFormat.Tiff;

            if(tiff2.SequenceEqual(bytes.Take(tiff2.Length)))
                return System.Drawing.Imaging.ImageFormat.Tiff;

            if(jpeg.SequenceEqual(bytes.Take(jpeg.Length)))
                return System.Drawing.Imaging.ImageFormat.Jpeg;

            if(jpeg2.SequenceEqual(bytes.Take(jpeg2.Length)))
                return System.Drawing.Imaging.ImageFormat.Jpeg;

            return null;
        }

        public static byte[] ImageToByteArray(Image im, ImageFormat format) {
            if(im != null) {
                MemoryStream MS = new MemoryStream();
                im.Save(MS, format);
                MS.Flush();
                return MS.ToArray();
            }
            return null;
        }

        # endregion

    

        public static bool isSQL_Number(Type t) {
           
            if(t == typeof(Int16) ||
                t == typeof(Int32) ||
                t == typeof(Int64) ||
                t == typeof(Decimal) ||
                t == typeof(Double) ||
                t == typeof(Single)) return true;
            return false;
        }

        public static bool isSQL_Text(Type t) {
            if(t == typeof(String) ||
               t == typeof(Char)) return true;
            return false;
        }


        public static DataRow getKeyRow(DataTable dataTable, object key, String keyColumn) {
            if(dataTable == null || key == null || key.ToString().Length == 0 ||
              !dataTable.Columns.Contains(keyColumn)) {
                return null;
            }
            try {
                object key2 = Convert.ChangeType(key, dataTable.Columns[keyColumn].DataType);
                foreach(DataRow row in dataTable.Rows) {
                    if(row[keyColumn].Equals(key2)) return row;
                }
            } catch(Exception) {
                return null;
            }
            return null;
        }
        public static object getKeyValue(DataTable dataTable, object key, String keyColumn, String valueColumn) {
            if(dataTable == null || key == null || key.ToString().Length == 0 ||
              !dataTable.Columns.Contains(keyColumn) ||
              !dataTable.Columns.Contains(valueColumn)) {
                return null;
            }
            try {
                object key2 = Convert.ChangeType(key, dataTable.Columns[keyColumn].DataType);
                foreach(DataRow row in dataTable.Rows) { 
                    if(row[keyColumn].Equals(key2)) return row[valueColumn];
                }
                /*
                Type columnType = dataTable.Columns[keyColumn].DataType;
                String expression;
                if(columnType == typeof(String) || columnType == typeof(Char)) {
                    expression = String.Format("{0} = '{1}'", keyColumn, key);
                } else {
                    expression = String.Format("{0} = {1}", keyColumn, key);
                }
                DataRow[] rows = dataTable.Select(expression);
                if(rows.Length > 0) return rows[0][valueColumn];
                 */
            } catch(Exception) {
                return false;
            }
            return null;
        }


        public static bool hasKeyValue(DataTable dataTable, object key, String keyColumn, String valueColumn) {
            if(dataTable == null || key == null || key.ToString().Length == 0 ||
              !dataTable.Columns.Contains(keyColumn) ||
              !dataTable.Columns.Contains(valueColumn)) {
                return false;
            }
            try {
                object convertedKey = Convert.ChangeType(key, dataTable.Columns[keyColumn].DataType);
                foreach(DataRow row in dataTable.Rows) {
                    if(row[keyColumn] == convertedKey &&
                       row[valueColumn] != null &&
                       row[valueColumn] != DBNull.Value) return true;

                }
            
            
            } catch {
                return false;
            }
            
            return false;
        }



        public static bool hasValue(DataColumn dataColumn, object value) {
            if(dataColumn == null) return false;
            DataTable dt = dataColumn.Table;
            try {
                object convertedValue = Convert.ChangeType(value, dataColumn.DataType);
                foreach(DataRow row in dt.Rows) {
                    if(row[dataColumn.ColumnName].Equals(convertedValue)) return true;
                }
            } catch {
                return false;
            }

           
            return false;
        }


        #region Byte-Array Converters

        public static  Double[] ToDouble(Byte[] byteArray) {
            byte size = 8;
            if((byteArray.LongLength % size) != 0) throw new Exception("wrong data type");
            Double[] a = new Double[byteArray.LongLength / size];
            for(int i = 0; i < a.LongLength; i++) {
                a[i] = BitConverter.ToDouble(byteArray, i*size);
            }
            return a;
        }

        public static  Single[] ToSingle(Byte[] byteArray) {
            byte size = 4;
            if((byteArray.LongLength % size) != 0) throw new Exception("wrong data type");
            Single[] a = new Single[byteArray.LongLength / size];
            for(int i = 0; i < a.LongLength; i++) {
                a[i] = BitConverter.ToSingle(byteArray, i*size);
            }
            return a;
        }

        public static  Int16[] ToInt16(Byte[] byteArray) {
            byte size = 2;
            if((byteArray.LongLength % size) != 0) throw new Exception("wrong data type");
            Int16[] a = new Int16[byteArray.LongLength / size];
            for(int i = 0; i < a.LongLength; i++) {
                a[i] = BitConverter.ToInt16(byteArray, i*size);
            }
            return a;
        }

        public static  Int32[] ToInt32(Byte[] byteArray) {
            byte size = 4;
            if((byteArray.LongLength % size) != 0) throw new Exception("wrong data type");
            Int32[] a = new Int32[byteArray.LongLength / size];
            for(int i = 0; i < a.LongLength; i++) {
                a[i] = BitConverter.ToInt32(byteArray, i*size);
            }
            return a;
        }

        public static  Int64[] ToInt64(Byte[] byteArray) {
            byte size = 8;
            if((byteArray.LongLength % size) != 0) throw new Exception("wrong data type");
            Int64[] a = new Int64[byteArray.LongLength / size];
            for(int i = 0; i < a.LongLength; i++) {
                a[i] = BitConverter.ToInt64(byteArray, i*size);
            }
            return a;
        }

        public static  UInt16[] ToUInt16(Byte[] byteArray) {
            byte size = 2;
            if((byteArray.LongLength % size) != 0) throw new Exception("wrong data type");
            UInt16[] a = new UInt16[byteArray.LongLength / size];
            for(int i = 0; i < a.LongLength; i++) {
                a[i] = BitConverter.ToUInt16(byteArray, i*size);
            }
            return a;
        }

        public static  UInt32[] ToUInt32(Byte[] byteArray) {
            byte size = 4;
            if((byteArray.LongLength % size) != 0) throw new Exception("wrong data type");
            UInt32[] a = new UInt32[byteArray.LongLength / size];
            for(int i = 0; i < a.LongLength; i++) {
                a[i] = BitConverter.ToUInt32(byteArray, i*size);
            }
            return a;
        }

        public static  UInt64[] ToUInt64(Byte[] byteArray) {
            byte size = 8;
            if((byteArray.LongLength % size) != 0) throw new Exception("wrong data type");
            UInt64[] a = new UInt64[byteArray.LongLength / size];
            for(int i = 0; i < a.LongLength; i++) {
                a[i] = BitConverter.ToUInt64(byteArray, i*size);
            }
            return a;
        }

       

        public static  Boolean[] ToBoolean(Byte[] byteArray) {
            byte size = 1;
            if((byteArray.LongLength % size) != 0) throw new Exception("wrong data type");
            Boolean[] a = new Boolean[byteArray.LongLength / size];
            for(int i = 0; i < a.LongLength; i++) {
                a[i] = BitConverter.ToBoolean(byteArray, i*size);
            }
            return a;
        }
        public static Char[] ToChar(Byte[] byteArray) {
            byte size = 2;
            if((byteArray.LongLength % size) != 0) throw new Exception("wrong data type");
            Char[] a = new Char[byteArray.LongLength / size];
            for(int i = 0; i < a.LongLength; i++) {
                a[i] = BitConverter.ToChar(byteArray, i*size);
            }
            return a;
        }

       
        #endregion


        #region Data Parser
        

        #endregion
        

        public static T[] ConcatArrays<T>(params T[][] list) {
            var result = new T[list.Sum(a => a.Length)];
            int offset = 0;
            for(int x = 0; x < list.Length; x++) {
                list[x].CopyTo(result, offset);
                offset += list[x].Length;
            }
            return result;
        }

    }

      

}
